if (null == window.GTBPreviewer) {
// -- Constructor --
/**
 * constructor of class GTBPreviewer which is used to preview GTB ads.
 * @param object in_ads the GTB ads to be previewed
 */
var GTBPreviewer = function(inAds){
    this.initialize(inAds);
    this.adBlock = null;
    this.adBox = null; //the table containing the ads
    this.adCells = new Array(); //the cells DOM Node of the ad
    this.adTitle = null; //the title DOM Node
    this.adBrokerLink = null; //the broker link DOM Node
    this.adBoxBody = this.headCell = this.footCell = null; //adBox components
    this.index = ++GTBPreviewer.index; //The unique ID in the page
};

// -- API --
//Overwrites this function to receive size change event. The function will be invoked
//and get the new width and height when the size has been changed.
GTBPreviewer.prototype.onSizeChange = function(width, height){ return; };

// Sets On Click Handler
GTBPreviewer.prototype.setHeadOnClickHandler = function(handler){
    this.adBox.tHead.onclick = this.headHandler = handler;
}
GTBPreviewer.prototype.setBodyOnClickHandler = function(handler){
    this.adBoxBody.onclick = this.bodyHandler = handler;
}
GTBPreviewer.prototype.setBrokerLinkOnClickHandler = function(handler){
    this.adBox.tFoot.onclick = this.brokerLinkHandler = handler;
}

//Initializes the previewer
GTBPreviewer.prototype.initialize = function(inAds) {
    this.spacer = inAds.spacer;
    this.location = inAds.location;
    this.title = inAds.payment.title;
    this.themeId = inAds.space.themeId;
    this.rating = inAds.space.rating;
    var style = inAds.space.style;
    this.size = null;
    if ((null != style.width) && (null != style.height)) {
        this.size = {
            width: style.width,
            height: style.height
        };
    }
    this.view = (GTBPreviewer.VERTICAL == style.view) ? GTBPreviewer.VERTICAL : GTBPreviewer.HORIZONTAL;
    this.cells = style.cells;
    this.imageSize = {
        width: style.imageWidth,
        height:style.imageHeight
    };
    this.components = {
        handle: style.showHandle,
        age: style.showAge,
        city: style.showLocation,
        headline: style.showHeadline,
        brokerLink: style.showBrokerLink,
        image: style.showImage,
        header: style.showHeader
    };
    this.style = {
        background: style.backgroundColor,
        head: GTBPreviewer.styleStringToObject(style.headStyle),
        body: GTBPreviewer.styleStringToObject(style.bodyStyle),
        brokerLink: GTBPreviewer.styleStringToObject(style.brokerLinkStyle)
    };
    this.requestURL = inAds.requestURL;
    this.baseURL = inAds.baseURL;
};

//Sets the size of the ads. Sets to auto if there's no argument.
GTBPreviewer.prototype.setSize = function(width, height){
    if (0 == arguments.length) {
        this.size = null;
    }
    else {
        width = parseInt(width);
        height = parseInt(height);
        if (isNaN(width) || isNaN(height)) {
            return;
        }
        this.size = {
            width: width,
            height: height
        };
    }
    this.updateSize();
};

//Sets orientation.
GTBPreviewer.HORIZONTAL = 'horizontal';
GTBPreviewer.VERTICAL = 'vertical';
GTBPreviewer.prototype.setView = function(view){
    if (1 > this.cells) {
        return;
    }
    var resize = false;
    if ((GTBPreviewer.HORIZONTAL == view) && (GTBPreviewer.VERTICAL == this.view)) { //v to h
        this.headCell.colSpan = this.cells;
        this.footCell.colSpan = this.cells;
        var rows = this.adBoxBody.rows;
        var rowsLength = rows.length;
        for (var i = 1; i < rowsLength; ++i) {
            rows[0].appendChild(rows[i].cells[0]);
        }
        for (var i = rowsLength - 1; 1 <= i; --i) {
            this.adBoxBody.deleteRow(i);
        }
        this.view = view;
        resize = true;
    }
    else if ((GTBPreviewer.VERTICAL == view) && (GTBPreviewer.HORIZONTAL == this.view)) { //h to v
        this.headCell.colSpan = 1;
        this.footCell.colSpan = 1;
        var cells = this.adBoxBody.rows[0].cells;
        var cellsLength = cells.length;
        for (var i = cellsLength - 1; 1 <= i; --i) {
            this.adBoxBody.insertRow(1).appendChild(cells[i]);
        }
        this.view = view;
        resize = true;
    }
    if (resize) {
        this.updateSize();
    }
};

//Sets Cells Count.
GTBPreviewer.prototype.setCount = function(count) {
    count = parseInt(count);
    if (isNaN(count) || (count == this.cells)) {
        return;
    }
    var resize = false;
    var isVertical = (this.view == GTBPreviewer.VERTICAL);
    if (count < this.cells) {
        if (isVertical) {
            for (var i = this.cells - 1; count <= i; --i) {
                this.adCells[i] = this.adCells[i].parentNode.removeChild(this.adCells[i]);
                this.adBoxBody.deleteRow(i);
            }
        }
        else {
            for (var i = this.cells - 1; count <= i; --i) {
                this.adCells[i] = this.adCells[i].parentNode.removeChild(this.adCells[i]);
                this.adBoxBody.rows[0].deleteCell(i);
            }
        }
        this.cells = count;
        this.headCell.colSpan = this.footCell.colSpan = isVertical ? 1 : count;
        resize = true;
    }
    else if (count <= this.adCells.length) {
        if (isVertical) {
            for (var i = this.cells; i < count; ++i) {
                this.adBoxBody.insertRow(i).insertCell(0).appendChild(this.adCells[i]);
            }
        }
        else {
            for (var i = this.cells; i < count; ++i) {
                this.adBoxBody.rows[0].insertCell(i).appendChild(this.adCells[i]);
            }
        }
        this.cells = count;
        this.headCell.colSpan = this.footCell.colSpan = isVertical ? 1 : count;
        this.clipTable(this.adBox);
        resize = true;
    }
    else {
        for (var i = this.cells; i < count; ++i) {
            if (i >= this.adCells.length) {
                this.adCells[i] = this.createCell();
            }
            if (GTBPreviewer.VERTICAL != this.view) {
                this.adBoxBody.rows[0].insertCell(i).appendChild(this.adCells[i]);
            }
            else {
                this.adBoxBody.insertRow(i).insertCell(0).appendChild(this.adCells[i]);
            }
        }
        this.headCell.colSpan = this.footCell.colSpan = isVertical ? 1 : count;
        this.clipTable(this.adBox);
        this.cells = count;
        this.renderEntries();
    }
    if (resize) {
        this.updateSize();
    }
};

//Sets Component. Shows it or hiddens.
GTBPreviewer.prototype.setComponent = function(componentName, isShow){
    if ('location' == componentName) {
        componentName = 'city';
    }
    if ('brokerlink' == componentName) {
        componentName = 'brokerLink';
    }
    if(componentName in this.components) {
        this.components[componentName] = isShow;
    }
    for (var i = 0; i < this.adCells.length; ++i) {
        for (var name in this.components) {
            if (('brokerLink' != name) && ('header' != name)) {
                this.adCells[i][name].style.display = this.components[name] ? 'block' : 'none';
            }
        }
    }
    this.adBrokerLink.parentNode.style.display = this.components.brokerLink ? '' : 'none';
    this.adTitle.parentNode.style.display = this.components.header ? '' : 'none';
    this.updateSize();
};

//Sets Image Size
GTBPreviewer.prototype.setImageSize = function(width, height){
    width = parseInt(width) > 0 ? parseInt(width) : 1;
    height = parseInt(height) > 0 ? parseInt(height) : 1;
    if ((this.imageSize.width != width) || (this.imageSize.height != height)) {
        this.imageSize = {
            width: width,
            height:height
        };
        for (var i = 0; i < this.adCells.length; ++i) {
            var gtbImage = this.adCells[i].image.getElementsByTagName('img')[0];
            var newSrc = gtbImage.src;
            newSrc = newSrc.substring(0, newSrc.indexOf('?'));
            newSrc += this.baseURL;
            newSrc += '&id=' + this.adCells[i].imageId;
            newSrc += '&width=' + this.imageSize.width;
            newSrc += '&height=' + this.imageSize.height;
            gtbImage.src = newSrc;
            gtbImage.style.width = this.imageSize.width + 'px';
            gtbImage.style.height = this.imageSize.height + 'px';
        }
        this.updateSize();
    }
};

//Sets Title.
GTBPreviewer.prototype.setTitle = function(title){
    this.title = title;
    var city = (0 < this.location.nearbyCities.length) ?
            this.location.nearbyCities[Math.floor(Math.random() * this.location.nearbyCities.length)] :
            this.location.cityName;
    this.adTitle.firstChild.data = this.adTitle.bak = title.replace(/\{city\}/ig, this.location.cityName).replace(/\{cities\}/ig, city);
    this.updateSize();
};

//Sets Theme
GTBPreviewer.prototype.setTheme = function(themeId){
    if (this.themeId != themeId) {
        this.themeId = themeId;
        this.refresh();
    }
};

//Sets Background
GTBPreviewer.prototype.setBackgroundColor = function(color){
    this.style.background = color;
    this.clipTable(this.adBox);
};

//Sets the style of each part
//Part Constant
GTBPreviewer.HEAD = 'head';
GTBPreviewer.BODY = 'body';
GTBPreviewer.BROKER_LINK = 'brokerLink';
//Sets the attribute named 'name' of the 'part' to 'value'
GTBPreviewer.prototype.setStyle = function(part, name, value){
    if (part in this.style) {
        this.style[part][name] = value;
    }
    switch (part) {
        case GTBPreviewer.HEAD: {
            for (var styleName in this.style.head) {
                this.adTitle.style[styleName] = this.style.head[styleName];
            }
        }
        break;
        case GTBPreviewer.BODY: {
            for (var styleName in this.style.body) {
                for (var i = 0; i < this.adCells.length; ++i) {
                    this.adCells[i].textBlock.style[styleName] = this.style.body[styleName];
                }
            }
        }
        break;
        case GTBPreviewer.BROKER_LINK: {
            for (var styleName in this.style.brokerLink) {
                this.adBrokerLink.style[styleName] = this.style.brokerLink[styleName];
                this.adBrokerLink.firstChild.style[styleName] = this.style.brokerLink[styleName];
            }
        }
        break;
        default:
        break;
    }
    this.updateSize();
};
// END OF API //

// -- Class Data Member --
//The unique index for every GTB previewer in one page
GTBPreviewer.index = 0;

//Space Constant
GTBPreviewer.CELL_PADDING = 3; // Padding of the table cell
GTBPreviewer.LINE_SPACING = 3; // Spacing between each line
GTBPreviewer.BODY_PADDING = 5; // Space between title and body, body and broker link.
GTBPreviewer.LENTH_ADJUST = 2 * GTBPreviewer.CELL_PADDING;

// -- Class Method --
//Translates the style string to object
GTBPreviewer.styleStringToObject = function(styleString){
    objStyle = new Object();
    if ((null != styleString) && 0 < (styleString.length)) {
        var stylePairs = styleString.split(';');
        var indexOfPair = 0;
        var name = '';
        var value = '';
        for (var i = 0; i < stylePairs.length; ++i) {
            indexOfPair = stylePairs[i].indexOf(':');
            if (0 < indexOfPair) {
                name = stylePairs[i].substr(0, indexOfPair);
                value = stylePairs[i].substr(indexOfPair + 1);
                objStyle[name] = value;
            }
        }
    }

    return objStyle;
};

//Translates the style object to style
GTBPreviewer.styleObjectToString = function(styleObject){
    strStyle = '';
    if (null != styleObject) {
        for (var styleName in styleObject) {
            if ('' != strStyle) {
                strStyle += ';';
            }
            strStyle += styleName + ':' + styleObject[styleName];
        }
    }

    return strStyle;
};

// -- Instance Method --
//renders the GTB
GTBPreviewer.prototype.render = function(){
    this.adTitle = this.createTitle();
    this.adCells = this.createCells();
    this.adBrokerLink = this.createBrokerLink();

    this.adBox = this.createAdBox(this.adTitle, this.adCells, this.adBrokerLink);
    var gtbAdBlockId = 'gtb_ad_block_' + this.index;
    document.write('<div id="' + gtbAdBlockId + '"></div>');
    this.adBlock = document.getElementById(gtbAdBlockId);
    this.adBlock.style.border = 'none';
    this.adBlock.style.margin = this.adBlock.style.padding = '0px';
    this.adBlock.previewer = this;
    this.adBlock.appendChild(this.adBox);
    this.adBlock.style.overflow = 'hidden';
    this.renderEntries();
};

GTBPreviewer.prototype.refresh = function(){
    this.adBlock.removeChild(this.adBox);

    this.adTitle = this.createTitle();
    this.adCells = this.createCells();
    this.adBrokerLink = this.createBrokerLink();
    this.adBox = this.createAdBox(this.adTitle, this.adCells, this.adBrokerLink);
    this.adBlock.appendChild(this.adBox);
    this.adBlock.style.overflow = 'hidden';
    if (this.headHandler) {
        this.setHeadOnClickHandler(this.headHandler);
    }
    if (this.bodyHandler) {
        this.setBodyOnClickHandler(this.bodyHandler);
    }
    if (this.brokerLinkHandler) {
        this.setBrokerLinkOnClickHandler(this.brokerLinkHandler);
    }
    this.renderEntries();
};

//Gets the entries by AJAX
GTBPreviewer.prototype.renderEntries = function() {
    var data = 'theme_id=' + escape(this.themeId) +
        '&rating=' + escape(this.rating) +
        '&cells=' + escape(this.cells) +
        '&city=' + escape(this.location.cityName);
    if (('latitude' in this.location) && ('longitude' in this.location)) {
        data += '&latitude=' + escape(this.location.latitude) +
                '&longitude=' + escape(this.location.longitude);
    }
    if ('' != this.requestURL) {
        new AjaxRequest(this.requestURL + this.baseURL, data, this);
    }
}

//Creates each part respectively
GTBPreviewer.prototype.createTitle = function(){
    var adTitle = createDiv();
    adTitle.style.paddingBottom = GTBPreviewer.BODY_PADDING + 'px';
    adTitle.previewer = this; //reference to the previewer
    for (var styleName in this.style.head) {
        adTitle.style[styleName] = this.style.head[styleName];
    }
    var city = (0 < this.location.nearbyCities.length) ?
        this.location.nearbyCities[Math.floor(Math.random() * this.location.nearbyCities.length)] :
        this.location.cityName;
    adTitle.bak = this.title.replace(/\{city\}/ig, this.location.cityName).replace(/\{cities\}/ig, city);
    adTitle.appendChild(document.createTextNode(adTitle.bak));

    return adTitle;
};
GTBPreviewer.prototype.createCell = function() {
    var adCell = createDiv();
    var isShow = this.components;

    adCell.image = createDiv();
    adCell.image.innerHTML = '<img src="' + this.spacer + '" style="border:none;width:' + this.imageSize.width +
            'px;height:' + this.imageSize.height + 'px" />';
    adCell.imageId = 0;
    adCell.image.style.textAlign = 'center';
    adCell.image.style.display = isShow.image ? 'block' : 'none';
    adCell.appendChild(adCell.image);

    adCell.textBlock = createDiv();
    adCell.handle = document.createElement('span');
    adCell.age = document.createElement('span');
    adCell.city = document.createElement('span');
    adCell.headline = document.createElement('span');
    adCell.handle.innerHTML = adCell.handle.bak = textHandle;
    adCell.age.innerHTML = adCell.age.bak = textAgeEighteen;
    adCell.city.innerHTML = adCell.city.bak = textLocation;
    adCell.headline.innerHTML = adCell.headline.bak = textHeadline;
    adCell.handle.style.display = isShow.handle ? 'block' : 'none';
    adCell.textBlock.appendChild(adCell.handle);
    adCell.age.style.display = isShow.age ? 'block' : 'none';
    adCell.textBlock.appendChild(adCell.age);
    adCell.city.style.display = isShow.city ? 'block' : 'none';
    adCell.textBlock.appendChild(adCell.city);
    adCell.headline.style.display = isShow.headline ? 'block' : 'none';
    adCell.textBlock.appendChild(adCell.headline);
    adCell.handle.style.padding = adCell.city.style.padding = GTBPreviewer.CELL_PADDING + 'px 0px';
    adCell.age.style.padding = adCell.headline.style.padding = GTBPreviewer.CELL_PADDING + 'px 0px';
    adCell.appendChild(adCell.textBlock);

    adCell.previewer = this;
    for (var styleName in this.style.body) {
        adCell.textBlock.style[styleName] = this.style.body[styleName];
    }

    return adCell;
}
GTBPreviewer.prototype.createCells = function(){
    adCells = new Array();
    for (var i = 0; i < this.cells; ++i) {
        adCells[i] = this.createCell();
    }

    return adCells;
};
GTBPreviewer.prototype.createBrokerLink = function(){
    var brokerLink = createDiv();
    brokerLink.style.paddingTop = GTBPreviewer.BODY_PADDING + 'px';
    var spanElement = document.createElement('span');
    for (var styleName in this.style.brokerLink) {
        brokerLink.style[styleName] = this.style.brokerLink[styleName];
        spanElement.style[styleName] = this.style.brokerLink[styleName];
    }
    brokerLink.previewer = this;
    spanElement.bak = spanElement.innerHTML = advertiseHere;
    spanElement.style.cursor = 'pointer';
    brokerLink.appendChild(spanElement);

    return brokerLink;
};

//Creates the Table
GTBPreviewer.prototype.createAdBox = function(adTitle, adCells, adBrokerLink){
    var adBox = document.createElement('table');
    adBox.previewer = this; //reference to the previewer
    if (GTBPreviewer.HORIZONTAL == this.view) {
        var bodyRow = adBox.insertRow(0);
        for (var i = 0; i < adCells.length; ++i) {
            bodyRow.insertCell(i).appendChild(adCells[i]);
        }
    }
    else {
        for (var i = 0; i < adCells.length; ++i) {
            adBox.insertRow(i).insertCell(0).appendChild(adCells[i]);
        }
    }
    this.adBoxBody = adBox.tBodies[0];
    this.headCell = adBox.createTHead().insertRow(0).insertCell(0);
    this.headCell.appendChild(adTitle);
    this.footCell = adBox.createTFoot().insertRow(0).insertCell(0);
    this.footCell.appendChild(adBrokerLink);
    adTitle.parentNode.style.display = this.components.header ? '' : 'none';
    adBrokerLink.parentNode.style.display = this.components.brokerLink ? '' : 'none';
    if (GTBPreviewer.HORIZONTAL == this.view) {
        this.headCell.colSpan = this.cells;
        this.footCell.colSpan = this.cells;
    }
    this.adBoxBody.previewer = adBox.tHead.previewer = adBox.tFoot.previewer = this;
    this.clipTable(adBox);
    return adBox;
};

//Clips the table to make them uniform
GTBPreviewer.prototype.clipTable = function(tableElement){
    tableElement.border = '0px';
    tableElement.cellPadding = '0px';
    tableElement.cellSpacing = '0px';
    tableElement.style.border = 'none';
    tableElement.style.padding = '0px';
    tableElement.style.margin = '0px';
    tableElement.tHead.style.cursor = 'pointer';
    tableElement.tHead.style.backgroundColor = this.style.background;
    tableElement.tBodies[0].style.cursor = 'pointer';
    tableElement.tBodies[0].style.backgroundColor = this.style.background;
    tableElement.tFoot.style.backgroundColor = 'transparent';
    tableElement.tBodies[0].onmouseover = tableElement.tHead.onmouseover = function(){
        window.status = this.previewer.adTitle.bak;
        return true;
    }
    tableElement.tBodies[0].onmouseout = tableElement.tHead.onmouseout = function(){
        window.status = '';
    }

    var rows = tableElement.rows;
    for (var i = 0; i < rows.length; ++i) {
        rows[i].style.backgroundColor = this.style.background;
        var cells = rows[i].cells;
        for (var j = 0; j < cells.length; ++j) {
            cells[j].style.padding = GTBPreviewer.CELL_PADDING + 'px';
            cells[j].style.margin = '0px';
            cells[j].style.border = 'none';
            cells[j].style.verticalAlign = 'middle';
            cells[j].style.backgroundColor = this.style.background;
        }
    }
    var footRow = tableElement.tFoot.rows[0];
    footRow.style.backgroundColor = 'transparent';
    footRow.cells[0].style.backgroundColor = 'transparent';
};

//updates the size of the ad block and table, and wraps the text.
GTBPreviewer.prototype.updateSize = function(){
    //re arrange the layout.
    for (var i = 0; i < this.cells; ++i) {
        this.adCells[i].parentNode.style.width = 'auto';
    }
    for (var i = 0; i < this.adBox.rows.length; ++i) {
        this.adBox.rows[i].style.height = 'auto';
    }
    this.adBox.style.width = 'auto';
    this.adBox.style.height = 'auto';
    this.adTitle.firstChild.data = this.adTitle.bak;
    this.adTitle.style.height = 'auto';
    this.adTitle.style.overflow = 'visible';
    for (var i = 0; i < this.adCells.length; ++i) {
        this.adCells[i].handle.firstChild.data = this.adCells[i].handle.bak;
        this.adCells[i].age.firstChild.data = this.adCells[i].age.bak;
        this.adCells[i].city.firstChild.data = this.adCells[i].city.bak;
        this.adCells[i].headline.firstChild.data = this.adCells[i].headline.bak;
        this.adCells[i].style.height = 'auto';
        this.adCells[i].style.overflow = 'visible';
    }
    var percent = Math.floor(100 / this.headCell.colSpan) + '%';
    for (var i = 0; i < this.cells; ++i) {
        this.adCells[i].parentNode.style.width = percent;
    }

    if (null != this.size) {
        this.adBlock.style.width = this.adBox.style.width = this.size.width + 'px';
        this.adBlock.style.height = this.adBox.style.height = this.size.height + 'px';
    }
    else {
        this.adBlock.style.width = Math.min((2 * (this.imageSize.width + GTBPreviewer.LENTH_ADJUST) * this.headCell.colSpan), Math.round(1.2 * this.adBox.offsetWidth)) + 'px';
        this.adBlock.style.height = Math.round(1.2 * this.adBox.offsetHeight) + 'px';
    }

    this.stripText();

    if (null == this.size) {
        if (this.onOverflow) {
            this.onOverflow(false);
        }
        this.adBlock.style.width = this.adBox.offsetWidth + 'px';
        this.adBlock.style.height = this.adBox.offsetHeight + 'px';
        this.adBox.style.width = this.adBlock.style.width;
        this.adBox.style.height = this.adBlock.style.height;
    }
    else {
        var diff = this.adBox.offsetHeight - this.adBlock.offsetHeight;
        if (diff > 0) {
            this.stripTextToOneLine(this.adTitle, Math.max(1, this.adBlock.offsetWidth - GTBPreviewer.LENTH_ADJUST),
                                    parseInt(this.adBlock.style.height) - (this.adBox.tBodies[0].offsetHeight + this.adBox.tFoot.offsetHeight));
            this.adBox.tHead.rows[0].style.height = (this.adTitle.offsetHeight + GTBPreviewer.LENTH_ADJUST) + 'px';
        }
        diff = this.adBox.offsetHeight - this.adBlock.offsetHeight;
        if (this.onOverflow) {
            this.onOverflow((diff > 0) || (this.adBox.offsetWidth > this.adBlock.offsetWidth));
        }
        if (diff > 0) {
            var cellDiff = diff;
            if (GTBPreviewer.VERTICAL == this.view) {
                cellDiff /= Math.max(1, this.cells);
                cellDiff = Math.floor(cellDiff);
            }
            for (var i = 0; i < this.adCells.length; ++i) {
                this.adCells[i].style.overflow = 'hidden';
                this.adCells[i].style.height = Math.max(0, this.adCells[i].offsetHeight - cellDiff) + 'px';
            }
            diff = this.adBox.offsetHeight - this.adBlock.offsetHeight;
            if (diff > 0) {
                this.adTitle.style.overflow = 'hidden';
                this.adTitle.style.height = Math.max(0, this.adTitle.offsetHeight - diff) + 'px';
            }
        }
    }

    if (null != this.onSizeChange) {
        this.onSizeChange(this.adBlock.offsetWidth, this.adBlock.offsetHeight);
    }
	
    if (('function' == typeof isIE7) && isIE7()) {
        controlIE7Performance();
    }
};
GTBPreviewer.prototype.stripText = function(){
    var adjust = GTBPreviewer.LENTH_ADJUST;
    this.wrapText(this.adTitle, Math.max(1, this.adBlock.offsetWidth - adjust));
    this.stripTextToOneLine(this.adBrokerLink.firstChild, Math.max(1, this.adBlock.offsetWidth - adjust));
    var colWidth = Math.max(1, Math.floor(this.adBlock.offsetWidth / this.headCell.colSpan));
    colWidth = Math.max(this.imageSize.width, colWidth - adjust);
    for (var i = 0; i < this.cells; ++i) {
        this.stripTextToOneLine(this.adCells[i].handle, colWidth);
        this.stripTextToOneLine(this.adCells[i].age, colWidth);
        this.stripTextToOneLine(this.adCells[i].city, colWidth);
        this.stripTextToOneLine(this.adCells[i].headline, colWidth);
    }
    for (var name in this.components) {
        if (this.components[name] && ('brokerLink' != name) && ('header' != name)) {
            this.tidyHeight(name);
        }
    }
    this.adBox.tHead.rows[0].style.height = (this.adTitle.offsetHeight + adjust) + 'px';
    if (this.components.brokerLink) {
        this.adBox.tFoot.rows[0].style.height = (this.adBrokerLink.offsetHeight + adjust) + 'px';
    }

    if (null != this.size) {
        var diffHeight = this.adCells[0].parentNode.offsetHeight - this.adCells[0].offsetHeight - adjust;
        var names = new Array();
        for (var name in this.components) {
            if (('brokerLink' != name) && ('header' != name) && this.components[name]) {
                names[names.length] = name;
            }
        }
        if ((0 < diffHeight) && (0 < names.length)) {
            diffHeight /= (names.length * 2);
            diffHeight = parseInt(diffHeight);
            for (var i = 0; i < this.cells; ++i) {
                for (var j = 0; j < names.length; ++j) {
                    var component = this.adCells[i][names[j]];
                    var paddingVertical = parseInt(component.style.paddingTop) + diffHeight;
                    component.style.padding = paddingVertical + 'px 0px';
                }
            }
        }
    }
};
GTBPreviewer.prototype.wrapText = function(elementNode, width){
    elementNode.style.width = '1px';
    elementNode.firstChild.data = elementNode.bak;
    elementNode.style.overflow = "hidden";

    if (elementNode.scrollWidth > width) {
        var allText = elementNode.bak;
        elementNode.removeChild(elementNode.firstChild);
        var lastPointer = pointer = 0;
        var lineText = null;
        var strippedText = '';
        var lineNode = document.createTextNode('');
        elementNode.appendChild(lineNode);
        while (pointer < allText.length) {
            lastPointer = pointer;
            lineNode.data = lineText = '';
            while ((elementNode.scrollWidth < width) && (pointer < allText.length)) {
                lineText = lineNode.data;
                lineNode.data += allText.charAt(pointer++);
            }
            if (elementNode.scrollWidth > width) {
                --pointer;
            }
            else {
                lineText = lineNode.data;
            }
            if (lastPointer == pointer) {
                break;
            }
            strippedText += (lineText + ' ');
        }
        elementNode.replaceChild(document.createTextNode((null != strippedText) ? strippedText : allText), lineNode);
    }
    elementNode.style.overflow = 'visible';
    elementNode.style.width = elementNode.style.height = 'auto';
};

GTBPreviewer.prototype.stripTextToOneLine = function(elementNode, width, height){
    width = Math.floor(width);
    elementNode.style.width = width + 'px';
    elementNode.style.overflow = "hidden";

    var lineNode = elementNode.firstChild
    var original_data = lineNode.data;
    lineNode.data = 'jJ';
    var lineHeight = elementNode.scrollHeight * 1.5;
    if (('undefined' != typeof height) && (height > lineHeight)) {//if height been set means multiple lines
        lineHeight = Math.floor(height);
    }
    lineNode.data = original_data;
    while((lineNode.data.length > 3) && ((elementNode.scrollWidth > width) || (elementNode.scrollHeight > lineHeight))) {
        lineNode.data = lineNode.data.substr(0, lineNode.data.length - 3).replace(/\S\s*$/, '...');    
    }

    elementNode.style.overflow = 'visible';
    elementNode.style.width = elementNode.style.height = 'auto';
};

GTBPreviewer.prototype.tidyHeight = function(componentName){
    var maxHeight = 0;
    var paddingVertical = 0;
    for (var i = 0; i < this.cells; ++i) {
        var component = this.adCells[i][componentName];
        component.style.padding = GTBPreviewer.LINE_SPACING + 'px 0px';
        maxHeight = Math.max(maxHeight, component.offsetHeight);
    }
    if (1 < this.headCell.colSpan) {
        for (var i = 0; i < this.cells; ++i) {
            var component = this.adCells[i][componentName];
            paddingVertical = GTBPreviewer.LINE_SPACING + Math.floor((maxHeight - component.offsetHeight) / 2);
            component.style.padding = paddingVertical + 'px 0px';
        }
    }
};

//Handles the gtb ads XML response
GTBPreviewer.prototype.handleXML = function(XMLDoc){
    var getDataByTagName = function(entry, tagName){
        var e = entry.getElementsByTagName(tagName)[0];
        return e.firstChild ? e.firstChild.nodeValue : '';
    };

    var locationName = XMLDoc.getElementsByTagName('location');
    if (0 < locationName.length) {
        this.location.cityName = (locationName[0].firstChild) ? locationName[0].firstChild.nodeValue : '';
        if (/^\s*$/.test(this.location.cityName)) {
            this.location.cityName = 'Location';
        }
    }

    this.location.nearbyCities = [];
    var gtbEntries = XMLDoc.getElementsByTagName('entry');
    var cellsCount = this.adCells.length < gtbEntries.length ? this.adCells.length : gtbEntries.length;
    for (var i = 0; i < cellsCount; ++i) {
        var adCell = this.adCells[i];

        var imageSource = getDataByTagName(gtbEntries[i], 'image');
        adCell.image.getElementsByTagName('img')[0].src = imageSource + this.baseURL + '&id=' + getDataByTagName(gtbEntries[i], 'id') +
                '&width=' + this.imageSize.width + '&height=' + this.imageSize.height;
        adCell.imageId = getDataByTagName(gtbEntries[i], 'id');
        adCell.handle.bak = getDataByTagName(gtbEntries[i], 'handle');
        adCell.handle.replaceChild(document.createTextNode(adCell.handle.bak), adCell.handle.firstChild);
        adCell.age.bak = getDataByTagName(gtbEntries[i], 'age');
        adCell.age.replaceChild(document.createTextNode(adCell.age.bak), adCell.age.firstChild);
        adCell.city.bak = getDataByTagName(gtbEntries[i], 'city');
        if (/^\s*$/.test(adCell.city.bak)) {
            adCell.city.bak = 'Location';
        }
        this.location.nearbyCities[this.location.nearbyCities.length] = adCell.city.bak;
        adCell.city.replaceChild(document.createTextNode(adCell.city.bak), adCell.city.firstChild);
        adCell.headline.bak = getDataByTagName(gtbEntries[i], 'headline');
        adCell.headline.replaceChild(document.createTextNode(adCell.headline.bak), adCell.headline.firstChild);
    }

    var city = (0 < this.location.nearbyCities.length) ?
            this.location.nearbyCities[Math.floor(Math.random() * this.location.nearbyCities.length)] :
            this.location.cityName;
    this.adTitle.firstChild.data = this.adTitle.bak = this.title.replace(/\{city\}/ig, this.location.cityName).replace(/\{cities\}/ig, city);

    this.updateSize();
};

// -- Utility --
function createDiv(){
    var div = document.createElement('div');
    div.style.padding = '0px';
    div.style.margin = '0px';
    div.style.border = 'none';
    div.style.backgroundColor = 'transparent';
    return div;
};

// -- Ajax Utility --
/**
 * Constructor
 * @param string url the url where the request is sent to
 * @param string data the data to be sent to the server
 * @param function listener the object which has function handleXML to accept the XML Document
 */
var AjaxRequest = function(url, data, listener){
    var request = this.createRequest();
    request.onreadystatechange = function () {
        if ((4 == request.readyState) && (200 == request.status)) {
            listener.handleXML(request.responseXML);
            //alert(request.responseText); //check the response
        }
    };
    this.request = request;
    this.sendRequest(url, data);
};

AjaxRequest.prototype.createRequest = function(){
    var request = null;
    try {
        request = new XMLHttpRequest();
    }
    catch (e) {//microsoft
        try {
            request = new ActiveXObject('Msxml2.XMLHTTP');
        }
        catch (e) {//other microsoft
            try {
                request = new ActiveXObject('Microsoft.XMLHTTP');
            }
            catch (e) {
                request = null;
            }
        }
    }

    return request;
};

AjaxRequest.prototype.sendRequest = function(url, data){
    this.request.open('POST', url, true);
    this.request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    this.request.send(data);
};
}